﻿using GalaSoft.MvvmLight.CommandWpf;
using Microsoft.Practices.ServiceLocation;
using Mimi.Common;
using Mimi.Models.DataModels;
using Mimi.Models.UIModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mimi.ViewModels
{
    public class OverviewViewModel : BindingBase, IOverviewViewModel
    {
        #region Fields
        private IStorageManager storageManager;
        #endregion

        #region constants
        private const string c_overviewIdentity = "0001";
        #endregion

        #region Properties
        private double balance;
        /// <summary>
        /// <see cref="Balance"/>
        /// </summary>
        public double Balance
        {
            get { return balance; }
            set
            {
                Set(ref balance, value);
            }
        }

        private Currency defaultCurrency;
        /// <summary>
        /// <see cref="DefaultCurrency"/>
        /// </summary>
        public Currency DefaultCurrency
        {
            get { return defaultCurrency; }
            set
            {
                Set(ref defaultCurrency, value);
            }
        }

        private string addedBalance;
        /// <summary>
        /// <see cref="AddedBalance"/>
        /// </summary>
        public string AddedBalance
        {
            get { return addedBalance; }
            set
            {
                bool isValid = CheckValidAmount(value);

                if (isValid || string.IsNullOrEmpty(value))
                {
                    if (value.StartsWith("0") && value.Length > 1)
                    {
                        Set(ref addedBalance, value.Remove(0, 1));
                    }
                    else
                    {
                        Set(ref addedBalance, value);
                    }
                }
                else
                {
                    Set(ref addedBalance, "0");
                }
            }
        }

        private List<Income> incomes;
        /// <summary>
        /// <see cref="Incomes"/>
        /// </summary>
        public List<Income> Incomes
        {
            get { return incomes ?? (incomes = new List<Income>()); }
            set
            {
                incomes = value;
            }
        }

        private ObservableCollection<UIIncome> bindingIncomes;
        /// <summary>
        /// <see cref="BindingIncomes"/>
        /// </summary>
        public ObservableCollection<UIIncome> BindingIncomes
        {
            get { return bindingIncomes ?? (bindingIncomes = new ObservableCollection<UIIncome>()); }
            set
            {
                Set(ref bindingIncomes, value);
            }
        }

        private Stack<ExtendedBudget> justAddedIncomes;
        /// <summary>
        /// Keep the incomes that have added already
        /// </summary>
        public Stack<ExtendedBudget> JustAddedIncomes
        {
            get { return justAddedIncomes ?? (justAddedIncomes = new Stack<ExtendedBudget>()); }
            set
            {
                justAddedIncomes = value;
            }
        }
        #endregion

        #region Commands
        private RelayCommand increaseBalanceCommand;
        /// <summary>
        /// <see cref="IncreaseBalanceCommand"/>
        /// </summary>
        public RelayCommand IncreaseBalanceCommand
        {
            get { return increaseBalanceCommand ?? (increaseBalanceCommand = new RelayCommand(IncreaseBalance)); }
        }

        private RelayCommand undoCommand;
        /// <summary>
        /// <see cref="UndoCommand"/>
        /// </summary>
        public RelayCommand UndoCommand
        {
            get { return undoCommand ?? (undoCommand = new RelayCommand(Undo)); }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Constructor
        /// </summary>
        public OverviewViewModel(IStorageManager storageManager)
        {
            this.storageManager = storageManager;

            //Init data
            this.DefaultCurrency = Currency.VND;
        }
        #endregion

        #region Methods
        /// <summary>
        /// <see cref="UpdateBalance"/>
        /// </summary>
        /// <param name="expense"></param>
        public void UpdateBalance(Expense expense)
        {
            //Update balance
            this.Balance -= expense.Amount;
            storageManager.UpdateOverviewRowData(new Overview() { OverviewIdentity = c_overviewIdentity, TotalBalance = this.Balance.ToString() });

            //Update budgets
            ServiceLocator.Current.GetInstance<IBudgetViewModel>().UpdateBudgets(expense);
        }

        /// <summary>
        /// <see cref="CalculateBalance"/>
        /// </summary>
        /// <param name="budgetList"></param>
        public void CalculateBalance(IEnumerable<Purpose> budgetList)
        {
            double sum = 0;

            foreach (var budget in budgetList)
            {
                sum += budget.Budget;
            }

            Balance = sum;
        }

        /// <summary>
        /// <see cref="InitOverviewData"/>
        /// </summary>
        public void InitOverviewData()
        {
            Overview overview = storageManager.QueryOverviewData();

            if (overview != null)
            {
                this.Balance = double.Parse(overview.TotalBalance);
            }
            else
            {
            }

            Debug.WriteLine("Overview completed");
        }

        /// <summary>
        /// <see cref="LoadIncomeData"/>
        /// </summary>
        public async void LoadIncomeData()
        {
            IEnumerable<Income> incomeData = await storageManager.QueryIncomeData();

            foreach (var income in incomeData)
            {
                this.Incomes.Add(income);
                this.BindingIncomes.Add(new UIIncome(income));
            }

            Debug.WriteLine("Income completed");
        }

        /// <summary>
        /// Increase main balance
        /// </summary>
        private void IncreaseBalance()
        {
            if (string.IsNullOrEmpty(this.AddedBalance))
            {
                return;
            }

            double value = double.Parse(this.addedBalance);

            if (value == 0)
            {
                return;
            }
            else
            {
                Balance += value;
                IEnumerable<Income> incomeDetails = ServiceLocator.Current.GetInstance<IBudgetViewModel>().UpdateBudgetsWithNewBalance(value);

                ExtendedBudget extendedBudget = new ExtendedBudget() { ExtendedBalance = value };

                //Save income detail to database
                foreach (var detail in incomeDetails)
                {
                    this.Incomes.Add(detail);
                    detail.IncomeId = this.Incomes.Count.ToString("D10");
                    
                    this.BindingIncomes.Add(new UIIncome(detail));

                    extendedBudget.IncludedIncomes.Add(detail);

                    storageManager.AddIncomeData(detail);
                }

                JustAddedIncomes.Push(extendedBudget);

                //Update balance in database
                storageManager.UpdateOverviewRowData(new Overview() { OverviewIdentity = c_overviewIdentity, TotalBalance = this.Balance.ToString() });

                this.AddedBalance = "0";
            }
        }

        /// <summary>
        /// Check if input is valid
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private bool CheckValidAmount(string input)
        {
            double amount;

            return double.TryParse(input, out amount);
        }

        /// <summary>
        /// Undo
        /// </summary>
        private void Undo()
        {
            if (JustAddedIncomes.Count > 0)
            {
                ExtendedBudget extendedBudget = JustAddedIncomes.Pop();

                foreach (var income in extendedBudget.IncludedIncomes)
                {
                    storageManager.RemoveFromIncomeTable(income);

                    //Remove from data collection
                    this.Incomes.Remove(income);
                    this.BindingIncomes.Remove(this.BindingIncomes.FirstOrDefault((x) => x.IncomeId.Equals(income.IncomeId)));
                }

                Balance -= extendedBudget.ExtendedBalance;
                double value = 0 - extendedBudget.ExtendedBalance;

                ServiceLocator.Current.GetInstance<IBudgetViewModel>().UpdateBudgetsWithNewBalance(value);

                storageManager.UpdateOverviewRowData(new Overview() { OverviewIdentity = c_overviewIdentity, TotalBalance = this.Balance.ToString() });
            }
        }
        #endregion
    }
}
